home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / UpdateManager / ChangelogViewer.py < prev    next >
Text File  |  2009-11-02  |  12KB  |  292 lines

  1. # ReleaseNotesViewer.py
  2. #  
  3. #  Copyright (c) 2006 Sebastian Heinlein
  4. #                2007 Canonical
  5. #  
  6. #  Author: Sebastian Heinlein <sebastian.heinlein@web.de>
  7. #          Michael Vogt <michael.vogt@ubuntu.com>
  8. #
  9. #  This modul provides an inheritance of the gtk.TextView that is 
  10. #  aware of http URLs and allows to open them in a browser.
  11. #  It is based on the pygtk-demo "hypertext".
  12. #  This program is free software; you can redistribute it and/or 
  13. #  modify it under the terms of the GNU General Public License as 
  14. #  published by the Free Software Foundation; either version 2 of the
  15. #  License, or (at your option) any later version.
  16. #  This program is distributed in the hope that it will be useful,
  17. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. #  GNU General Public License for more details.
  20. #  You should have received a copy of the GNU General Public License
  21. #  along with this program; if not, write to the Free Software
  22. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  23. #  USA
  24.  
  25.  
  26. import pygtk
  27. import gtk
  28. import pango
  29. import subprocess
  30. import os
  31. from gettext import gettext as _
  32.  
  33. class ChangelogViewer(gtk.TextView):
  34.     def __init__(self, changelog=None):
  35.         """Init the ChangelogViewer as an Inheritance of the gtk.TextView"""
  36.         # init the parent
  37.         gtk.TextView.__init__(self)
  38.         # global hovering over link state
  39.         self.hovering = False
  40.         self.first = True
  41.         # setup the buffer and signals
  42.         self.set_property("editable", False)
  43.         self.set_cursor_visible(False)
  44.         # set some margin
  45.         self.set_right_margin(4)
  46.         self.set_left_margin(4)
  47.         self.set_pixels_above_lines(4)
  48.         self.buffer = gtk.TextBuffer()
  49.         self.set_buffer(self.buffer)
  50.         self.connect("button-press-event", self.button_press_event)
  51.         self.connect("motion-notify-event", self.motion_notify_event)
  52.         self.connect("visibility-notify-event", self.visibility_notify_event)
  53.         #self.buffer.connect("changed", self.search_links)
  54.         self.buffer.connect_after("insert-text", self.on_insert_text)
  55.         # search for links in the changelog and make them clickable
  56.         if changelog != None:
  57.             self.buffer.set_text(changelog)
  58.             
  59.     def create_context_menu(self, url):
  60.         """Create the context menu to be displayed when links are right clicked"""
  61.         self.menu = gtk.Menu()
  62.         
  63.         # create menu items
  64.         item_grey_link = gtk.MenuItem(url)
  65.         item_grey_link.connect("activate", self.handle_context_menu, "open", url)
  66.         item_seperator = gtk.MenuItem()
  67.         item_open_link = gtk.MenuItem(_("Open Link in Browser"))
  68.         item_open_link.connect("activate", self.handle_context_menu, "open", url)
  69.         item_copy_link = gtk.MenuItem(_("Copy Link to Clipboard"))
  70.         item_copy_link.connect("activate", self.handle_context_menu, "copy", url)
  71.         
  72.         # add menu items
  73.         self.menu.add(item_grey_link)
  74.         self.menu.add(item_seperator)
  75.         self.menu.add(item_open_link)
  76.         self.menu.add(item_copy_link)
  77.         self.menu.show_all()
  78.     
  79.     def handle_context_menu(self, menuitem, action, url):
  80.         """Handle activate event for the links' context menu"""
  81.         if action == "open":
  82.             self.open_url(url)
  83.         if action == "copy":
  84.             cb = gtk.Clipboard()
  85.             cb.set_text(url)
  86.             cb.store()
  87.  
  88.     def tag_link(self, start, end, url):
  89.         """Apply the tag that marks links to the specified buffer selection"""
  90.         tagged = False
  91.         tags = start.get_tags()
  92.         for tag in tags:
  93.             url = tag.get_data("url")
  94.             if url != "":
  95.                 return
  96.         tag = self.buffer.create_tag(None, foreground="blue",
  97.                                      underline=pango.UNDERLINE_SINGLE)
  98.         tag.set_data("url", url)
  99.         self.buffer.apply_tag(tag , start, end)
  100.  
  101.     def on_insert_text(self, buffer, iter_end, text, *args):
  102.         """Search for http URLs in newly inserted text  
  103.            and tag them accordingly"""
  104.  
  105.         # some convenient urls
  106.         MALONE = "https://launchpad.net/bugs/"
  107.         DEBIAN = "http://bugs.debian.org/"
  108.         CVE = "http://cve.mitre.org/cgi-bin/cvename.cgi?name="
  109.         # some convinient end-markers
  110.         ws = [" ","\t","\n"]
  111.         brak = [")","]",">"]
  112.         punct = [",","!",":"]
  113.         dot = ["."]+punct
  114.         dot_cr = [".\n"]
  115.  
  116.         # search items are start-str, list-of-end-strs, url-prefix
  117.         # a lot of this search is "TEH SUCK"(tm) because of limitations
  118.         # in iter.forward_search()
  119.         # - i.e. no insensitive searching, no regexp
  120.         search_items = [ ("http://", ws+brak+punct+dot_cr, "http://"),
  121.                          ("LP#", ws+brak+dot, MALONE),
  122.                          ("LP: #", ws+brak+dot, MALONE),
  123.                          ("lp: #", ws+brak+dot, MALONE),
  124.                          ("LP:#",  ws+brak+dot, MALONE),
  125.                          ("Malone: #", ws+brak+dot, MALONE),
  126.                          ("Malone:#", ws+brak+dot, MALONE),
  127.                          ("Ubuntu: #", ws+brak+dot, MALONE),
  128.                          ("Ubuntu:#", ws+brak+dot, MALONE),
  129.                          ("Closes: #",ws+brak+dot, DEBIAN),
  130.                          ("Closes:#",ws+brak+dot, DEBIAN),
  131.                          ("closes:#",ws+brak+dot, DEBIAN),
  132.                          ("closes: #",ws+brak+dot, DEBIAN),
  133.                          ("CVE-", ws+brak+dot, CVE),
  134.                        ]
  135.         # init
  136.         iter = buffer.get_iter_at_offset(iter_end.get_offset() - len(text))
  137.         iter_real_end = buffer.get_end_iter()
  138.  
  139.         # search for the next match in the buffer
  140.         for (start_str, end_list, url_prefix) in search_items:
  141.             while True:
  142.                 ret = iter.forward_search(start_str,
  143.                                           gtk.TEXT_SEARCH_VISIBLE_ONLY,
  144.                                           iter_end)
  145.                 # if we reach the end break the loop
  146.                 if not ret:
  147.                     break
  148.                 # get the position of the protocol prefix
  149.                 (match_start, match_end) = ret
  150.                 match_suffix = match_end.copy()
  151.                 match_tmp = match_end.copy()
  152.                 while True:
  153.                     # extend the selection to the complete search item
  154.                     if match_tmp.forward_char():
  155.                         text =  match_end.get_text(match_tmp)
  156.                         if text in end_list:
  157.                             break
  158.                         # move one char futher to get two char
  159.                         # end-markers (and back later) LP: #396393
  160.                         match_tmp.forward_char()
  161.                         text =  match_end.get_text(match_tmp)
  162.                         if text in end_list:
  163.                             break
  164.                         match_tmp.backward_char()
  165.                     else:
  166.                         break
  167.                     match_end = match_tmp.copy()
  168.  
  169.                 # call the tagging method for the complete URL
  170.                 url = url_prefix + match_suffix.get_text(match_end)
  171.  
  172.                 self.tag_link(match_start, match_end, url)
  173.                 # set the starting point for the next search
  174.                 iter = match_end
  175.  
  176.     def button_press_event(self, text_view, event):
  177.         """callback for mouse click events"""
  178.         # we only react on left or right mouse clicks
  179.         if event.button != 1 and event.button != 3:
  180.             return False
  181.  
  182.         # try to get a selection
  183.         try:
  184.             (start, end) = self.buffer.get_selection_bounds()
  185.         except ValueError:
  186.             pass
  187.         else:
  188.             if start.get_offset() != end.get_offset():
  189.                 return False
  190.  
  191.         # get the iter at the mouse position
  192.         (x, y) = self.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET,
  193.                                               int(event.x), int(event.y))
  194.         iter = self.get_iter_at_location(x, y)
  195.         
  196.         # call open_url or menu.popup if an URL is assigned to the iter
  197.         tags = iter.get_tags()
  198.         for tag in tags:
  199.             url = tag.get_data("url")
  200.             if url != None:
  201.                 if event.button == 1: 
  202.                     self.open_url(url)
  203.                     break
  204.                 if event.button == 3:
  205.                     self.create_context_menu(url)
  206.                     self.menu.popup(None, None, None, event.button, event.time)
  207.                     return True
  208.  
  209.     def open_url(self, url):
  210.         """Open the specified URL in a browser"""
  211.         # Find an appropiate browser
  212.         if os.path.exists("/usr/bin/exo-open"):
  213.             command = ["exo-open", url]
  214.         elif os.path.exists('/usr/bin/gnome-open'):
  215.             command = ['gnome-open', url]
  216.         else:
  217.             command = ['x-www-browser', url]
  218.  
  219.         # Avoid to run the browser as user root
  220.         if os.getuid() == 0 and os.environ.has_key('SUDO_USER'):
  221.             command = ['sudo', '-u', os.environ['SUDO_USER']] + command
  222.  
  223.         subprocess.Popen(command)
  224.  
  225.     def motion_notify_event(self, text_view, event):
  226.         """callback for the mouse movement event, that calls the
  227.            check_hovering method with the mouse postition coordiantes"""
  228.         x, y = text_view.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET,
  229.                                                  int(event.x), int(event.y))
  230.         self.check_hovering(x, y)
  231.         self.window.get_pointer()
  232.         return False
  233.     
  234.     def visibility_notify_event(self, text_view, event):
  235.         """callback if the widgets gets visible (e.g. moves to the foreground)
  236.            that calls the check_hovering method with the mouse position
  237.            coordinates"""
  238.         (wx, wy, mod) = text_view.window.get_pointer()
  239.         (bx, by) = text_view.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, wx,
  240.                                                      wy)
  241.         self.check_hovering(bx, by)
  242.         return False
  243.  
  244.     def check_hovering(self, x, y):
  245.         """Check if the mouse is above a tagged link and if yes show
  246.            a hand cursor"""
  247.         _hovering = False
  248.         # get the iter at the mouse position
  249.         iter = self.get_iter_at_location(x, y)
  250.         
  251.         # set _hovering if the iter has the tag "url"
  252.         tags = iter.get_tags()
  253.         for tag in tags:
  254.             url = tag.get_data("url")
  255.             if url != None:
  256.                 _hovering = True
  257.                 break
  258.  
  259.         # change the global hovering state
  260.         if _hovering != self.hovering or self.first == True:
  261.             self.first = False
  262.             self.hovering = _hovering
  263.             # Set the appropriate cursur icon
  264.             if self.hovering:
  265.                 self.get_window(gtk.TEXT_WINDOW_TEXT).\
  266.                         set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
  267.             else:
  268.                 self.get_window(gtk.TEXT_WINDOW_TEXT).\
  269.                         set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))
  270.  
  271.  
  272. if __name__ == "__main__":
  273.     w = gtk.Window()
  274.     cv = ChangelogViewer()
  275.     changes = cv.get_buffer()
  276.     changes.create_tag("versiontag", weight=pango.WEIGHT_BOLD)
  277.     changes.set_text("""
  278.  
  279. Version 6-14-0ubuntu1.9.04:
  280.  
  281.   * New upstream version. LP: #382918.
  282.     Release notes at http://java.sun.com/javase/6/webnotes/ReleaseNotes.html.
  283.  
  284. """)
  285.  
  286.     w.add(cv)
  287.     w.show_all()
  288.     gtk.main()
  289.